/**  
 * All rights Reserved, Designed By www.richfit.com
 * @Title:  AbsScanService.java   
 * @Package com.richfit.cuba.modular   
 * @Description:    TODO
 * @author: Alan
 * @date:   2019-03-03 16:40:09
 * @version V1.0 
 */
package com.richfit.cuba.modular;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.richfit.cuba.modular.common.ICommonService;
import com.richfit.cuba.modular.lsms.entity.SafeEncrypted;
import com.richfit.cuba.modular.lsms.entity.SafeReport;
import com.richfit.cuba.modular.lsms.entity.SafeReportData;
import com.richfit.cuba.modular.lsms.entity.SafeReportDetail;
import com.richfit.cuba.modular.lsms.util.DateUtils;
import com.richfit.cuba.modular.util.Certificates;
import com.richfit.cuba.modular.util.CommonUtils;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;

/**   
 * @ClassName:  AbsScanService   
 * @Description:TODO
 * @author: Alan
 * @date:   2019-03-03 16:40:09
 *     
 */
@Service
//@Transactional
public abstract class AbsScanService<T> implements IScanService {
	/**
	 * bussiness service
	 */
	private ICommonService<T> bizSerivce;
	
	@Value("${validate.page.limit}")
	protected Integer pageLimit;
	
	@Autowired
	protected CommonUtils commonUtils;
	
	private static final Log log = LogFactory.get();
	
	public AbsScanService(ICommonService<T> service) {
		this.bizSerivce = service;
	}
	
	
	/**   
	 * <p>Title: scanTable</p>   
	 * <p>Description: </p>      
	 * @see com.richfit.cuba.modular.IScanService#scanTable()   
	 * @author: Alan
	 * @date:   2019-03-03 16:42:25
	 */
	@Override
	public void scanTable() {
		Page<T> firstPage = new Page<T>(1, this.pageLimit);
		Page<T> page = this.selectTableData(firstPage);
		
		if(page.getPages()>0) {
			long pages = page.getPages();
			//TODO 这里加入日志保存代码，第一次只保存主表信息。
			SafeReport safeReport = initLog(page);
			for(int i=1; i<=pages; i++) {
				handleOnePage(page, safeReport);
				long nextPage = firstPage.getCurrent()+1L;
				if(nextPage <= pages) {
					firstPage.setCurrent(nextPage);
					page = this.selectTableData(firstPage);
				}
			}
		}else {
			try {
				commonUtils.insertNullData(getBusinessTable(), Certificates.getTableName(getClassT()));
			} catch (Exception e) {
				e.printStackTrace();
				log.info(e.getMessage());
			}
		}
		
	}

	/**   
	 * <p>Title: selectTableData</p>   
	 * <p>Description: </p>   
	 * @return   
	 * @see com.richfit.cuba.modular.IScanService#selectTableData()   
	 * @author: Alan
	 * @date:   2019-03-03 16:42:25
	 */
	public Page<T> selectTableData(Page<T> page) {
//		return (Page<T>) bizSerivce.page(page, getWrapperQuery());
//		page.setSearchCount(true);
		page = (Page<T>) bizSerivce.getCustomerPage(page, getWrapperQuery());
		return page;
	}
	
	
	
	private SafeReport initLog(Page<T> page) {
		Date day = DateUtils.truncate(DateUtils.getCubaDate(Calendar.getInstance().getTime()), Calendar.DATE);
		SafeReport safeReport = commonUtils.getSafeReportByDate(day, getBusinessTable());
		if(safeReport == null || StringUtils.isBlank(safeReport.getId())) {
			safeReport = new SafeReport();
			safeReport.setBusinessDate(day);
			safeReport.setBussinessTable(getBusinessTable());
			safeReport.setTableDataTotal(page.getTotal());
			safeReport.setCurrentScanDataCount(0);
			Date date = DateUtils.getCubaDate(Calendar.getInstance().getTime());
			safeReport.setCreateDate(date);
			safeReport.setUpdateDate(date);
			commonUtils.saveSaveReport(safeReport);
		}
		return safeReport;
	}
	
	/**
	 * 
	 * @Title: handleOnePage   
	 * @Description: handle one page data 
	 * @author: Alan
	 * @date:   2019-03-03 18:54:44
	 * @param page      
	 * @return  void      
	 * @throws null
	 */
	private void handleOnePage(Page<T> page, SafeReport safeReport) {
		List<T> list = page.getRecords();
		// init detail
		SafeReportDetail detail = new SafeReportDetail();
		detail.setReportId(safeReport.getId());
		detail.setSortNo(Long.valueOf(page.getCurrent()).intValue());
		detail.setItemTotal(list.size());
		List<SafeReportData> errorList=Lists.newArrayList();
		detail.setSafeReportDataList(errorList);
		
		// 循环判断数据是否有效以及正确
		long correctCount = 0l;
		for(T obj: list) {
			try {
				String key = getDataPrimaryKey(obj);
				if(key == null) {
					log.error("No key : "+obj);
					continue;
				}
				// 在加密表中查询
				SafeEncrypted safeObj=commonUtils.selectCiphertext(key);
				if(safeObj!=null) {
					// 获取当前业务库 数据
					String nowVal = Certificates.valueJoin(obj, safeObj.getEncryptedAttribute());
					// 解密获取原文
					String val = Certificates.handleStringData(Certificates.decrypt(safeObj.getEncryptedCertificate(), safeObj.getEncryptedField()));
					boolean flag = Certificates.isEqualEncrypted(val, nowVal);
					if(flag) {
						correctCount++;
					}else {
						SafeReportData detailObj=createNewDetailData(safeReport.getBussinessTable(), key, val, nowVal);
						detailObj = setBussinuessData(obj, detailObj);
						errorList.add(detailObj);
					}
				}else {
					correctCount++;
					// 原始数据存在业务库中，但是没有存在于加密表中。不设置合法原始值，即不设置需要反编译原始加密的值。
//					SafeReportData detailObj=createNewDetailData(safeReport.getBussinessTable(), key, null, nowVal);
//					errorList.add(detailObj);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		detail.setSuccessTotal(correctCount);
		detail.setFailTotal(detail.getItemTotal()-correctCount);
		detail.setCreateDate(DateUtils.getCubaDate(Calendar.getInstance().getTime()));
		
		try {
			// insert new detail data
			commonUtils.insertReportData(detail);
			// Update current scan total number
			safeReport.setUpdateDate(DateUtils.getCubaDate(Calendar.getInstance().getTime()));
			long currentScanDataCount = safeReport.getCurrentScanDataCount()+detail.getItemTotal();
			safeReport.setCurrentScanDataCount(currentScanDataCount);
			commonUtils.saveSaveReport(safeReport);
		} catch (Exception e) {
			e.printStackTrace();
			log.info(e.getMessage());
		}
	}
	
	private SafeReportData createNewDetailData(String bussinessTableName, String bussinessDataId, String oldVal, String newVal) {
		SafeReportData detailObj=new SafeReportData();
		Date date = DateUtils.getCubaDate(Calendar.getInstance().getTime());
		detailObj.setScanningTable(bussinessTableName);
		detailObj.setScanningTime(date);
		detailObj.setCreateDate(date);
		detailObj.setDataId(bussinessDataId);
		if(StringUtils.isNotBlank(oldVal)) {
			System.out.println("oldVal = "+oldVal);
			oldVal = Certificates.handleStringData(oldVal);
			detailObj.setLegalValue(new BigDecimal(oldVal));
		}
		if(StringUtils.isNotBlank(newVal)) {
			System.out.println("newVal = "+newVal);
			detailObj.setAbnomalValue(new BigDecimal(newVal));
		}
		return detailObj;
	}
	
	/**
	 * 
	 * @Title: getDataPrimaryKey   
	 * @Description: get the data primary key value 
	 * @author: Alan
	 * @date:   2019-03-03 17:09:19
	 * @param obj
	 * @return      
	 * @return  String      
	 * @throws null
	 */
	public String getDataPrimaryKey(T obj) {
		String keyVal = null;
		try {
			Class<?> clazz = getClassT();
			Field[] fields = clazz.getDeclaredFields();
			for (Field field : fields) {
				boolean isTableId = clazz.isAnnotationPresent(TableId.class);
				if(isTableId || "id".equalsIgnoreCase(field.getName())) {
					keyVal = ReflectUtil.invoke(obj, "get"+StringUtils.capitalize(field.getName()));
					break;
				}
			}
			if(keyVal == null) {
				try {
					keyVal = ReflectUtil.invoke(obj, "pkVal");
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return keyVal;
	};
	
	/**
	 * 
	 * @Title: getBusinessTable   
	 * @Description: get bussiness Table name 
	 * by Alan 2019-03-28 18:26:44
	 * @return      
	 * @return  String
	 */
	public String getBusinessTable() {
		try {
			return Certificates.getTableName(getClassT());
		} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException
				| InstantiationException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * set ORGANIZATION, Product, ABNOMAL_VALUE<br> 
	 * 设置 机构，产品，非法值
	 * @param obj bussiness data
	 * @return SafeReportData
	 */
	public abstract SafeReportData setBussinuessData(T obj, SafeReportData detailObj);
	
	/**
	 * 需要反编译原始加密的值，并将此数值设置到数据对象中。
	 * @param safeReportData 数据对象
	 * @param safeEncrypted 加密对象
	 * @return 数据对象
	 */
	public SafeReportData setLegalValue(SafeReportData safeReportData, SafeEncrypted safeEncrypted) {
		if(StringUtils.isNotBlank(safeEncrypted.getEncryptedField())) {
			String val = Certificates.decrypt(safeEncrypted.getEncryptedCertificate(), safeEncrypted.getEncryptedField());
			if(StringUtils.isNotBlank(val)) {
				String[] vals = val.split(Certificates.SPLIE_CHAR);
				safeReportData.setLegalValue(new BigDecimal(vals[0]));
			}
		}
		return safeReportData;
	}

	/**
	 * 
	 * @Title: getWrapperQuery   
	 * @Description: create QueryWrapper for query list 
	 * @author: Alan
	 * @date:   2019-03-03 16:54:40
	 * @return      
	 * @return  QueryWrapper<T>      
	 */
	public QueryWrapper<T> getWrapperQuery(){
		QueryWrapper<T> entity=new QueryWrapper<T>();
		//entity.eq("DEL_FLAG", ScanStatus.TABLE_DEFLAGE_NO);
		return entity;
	}

	/**
	 * 
	 * @Title: getClassT   
	 * @Description: TODO 
	 * @author: Alan
	 * @date:   2019-02-14 10:45:41
	 * @return
	 * @throws InstantiationException
	 * @throws IllegalAccessException      
	 *     Class<T>      
	 * @throws null
	 */
	private Class<T> getClassT() throws InstantiationException, IllegalAccessException {
		Type sType = getClass().getGenericSuperclass();
		Type[] generics = ((ParameterizedType) sType).getActualTypeArguments();
		@SuppressWarnings("unchecked")
		Class<T> mTClass = (Class<T>) (generics[0]);
		return mTClass;
	}
}
